/**
 * 							HEAP MANAGEMENT
 * @brief	  :manage heap common operations.
 * @author    :chy.
 * @note	  :this file manage the virtual memory
 * @comments  :manage heap common operations.
 */
#ifndef _K_MM_HEAP_C_
#define _K_MM_HEAP_C_

#include "k_mm_heap.h"
#include "mem_basedefine.h"

/**
 * 		MACROS
 */
// get next heap address
#define K_MM_HEAP_NXTBLOCKADDR(x) ((K_PHEAPBLOCK)((unsigned int)x + K_HEAPBLOCK_HEADERSIZE + x->hb_size))
extern void k_mem_mmu_free(void *vir_addr);

/**
 * @BRIEF	: GC for a heap
 * @PARAM	: heap
 * @RETRUN	: inc bytes
 * @NOTE	: if GC success, K_SYS_OS_HEAP_GCFREQ of heap will clear to 0
 */
int	k_mm_heap_gc(K_PHEAP heap)
{
	unsigned int oldsize = heap->heap_usedsize;
	unsigned int endaddress;
	K_PHEAPBLOCK p_cur_block,p_nxt_block,p_pre_block,p_block;

	if(heap->heap_gcfreq < K_SYS_OS_HEAP_GCFREQ)
	{
		return 0;
	}

	p_block = heap->heap_firsthb;
	if(p_block == 0)
	{
		// there's nothing to do!
		return 1;
	}

	endaddress = (unsigned int)heap + heap->heap_totalsize;
	p_cur_block = (K_PHEAPBLOCK)((unsigned int)heap + K_HEAP_HEADERSIZE);
	p_pre_block = 0;

	while((unsigned int)p_cur_block < endaddress)
	{
		if(p_cur_block->hb_mflag == K_HEAPBLOCK_HEADERFLAG_USING)
		{
			p_cur_block = K_MM_HEAP_NXTBLOCKADDR(p_cur_block);
			continue;
		}
		else
		{
			p_nxt_block = K_MM_HEAP_NXTBLOCKADDR(p_cur_block);
			while((unsigned int)p_nxt_block < endaddress)
			{
				if(p_nxt_block->hb_mflag == K_HEAPBLOCK_HEADERFLAG_USING)
				{
					break;
				}
				else
				{
					// join
					p_cur_block->hb_size += p_nxt_block->hb_size + K_HEAPBLOCK_HEADERSIZE;
					heap->heap_usedsize -= K_HEAPBLOCK_HEADERSIZE;
					p_nxt_block = K_MM_HEAP_NXTBLOCKADDR(p_nxt_block);
				}
			}
			p_cur_block->hb_next = p_pre_block;
			p_pre_block = p_cur_block;
			p_cur_block = p_nxt_block;
		}
	}

	heap->heap_firsthb = p_pre_block;
}

/**
 * @BRIEF	: free a heap
 * @PARAM	: heappool      heappool of process or OS
 * @RETRUN	: void
 * @NOTE	: this function will scan the heap pool of OS, to find
 *          : which heap can be freed.and free it.
 *          : just free one heap
 */
void k_mm_heap_freeheap(K_HEAPPOOL *heappool)
{
    K_PHEAP heap_cur = heappool->hp_firstheap;
    K_PHEAP heap_pre = 0;
	unsigned int f_count;
	unsigned int i;

    while(heap_cur != 0)
    {
        // there is only one free heap block, and
        // this block is unused. now we can free
        // current heap.
        if(heap_cur->heap_usedsize == (K_HEAP_HEADERSIZE + K_HEAPBLOCK_HEADERSIZE))
        {
            if(heap_pre == 0)
            {
                // we free the first heap in heap pool
                heappool->hp_firstheap = heap_cur->heap_next;
            }
            else
            {
                heap_pre->heap_next = heap_cur->heap_next;
            }
            heappool->hp_heapcount--;
            heappool->hpool_size -= heap_cur->heap_totalsize;
			if((heap_cur->heap_totalsize & 0xFFF) != 0 || ((unsigned int)heap_cur & 0xFFF) != 0 )
			{
				// there must be some errors! size and address must be bound of 4KB
				return;
			}
			f_count = heap_cur->heap_totalsize >> 12;	// compute page count
            for(i = 0; i < f_count; i++)
			{
				k_mem_mmu_free((void *)((unsigned int)heap_cur + (i << 12)));
			}
            return;
        }
        heap_pre = heap_cur;
        heap_cur = heap_cur->heap_next;
    }
}

#endif  /* _K_MM_HEAP_C_ */

